
import time
from django.utils import timezone
from django.shortcuts import render
from django.views import View
from django_redis import get_redis_connection
from django.db import transaction
from django import http

import json
from decimal import Decimal
# Create your views here.
from meiduo_mall.utils.views import LoginRequiredMixin, LoginRequiredJsonMixin
from meiduo_mall.utils.response_code import RETCODE
from users.models import Address

from orders.models import OrderInfo, OrderGoods
from goods.models import SKU


class OrderSuccessView(LoginRequiredMixin, View):
    """提交订单成功"""

    def get(self, request):
        """提供提交订单成功页面"""
        order_id = request.GET.get('order_id')
        payment_amount = request.GET.get('payment_amount')
        pay_method = request.GET.get('pay_method')

        context = {
            'order_id': order_id,
            'payment_amount': payment_amount,
            'pay_method': pay_method
        }

        return render(request, 'order_success.html', context)


class OrderCommitView(LoginRequiredJsonMixin, View):
    """订单提交"""

    def post(self, request):
        """保存订单基本信息和订单商品信息"""
        # 接受参数
        json_dict = json.loads(request.body.decode())
        address_id = json_dict.get('address_id')
        pay_method = json_dict.get('pay_method')

        # 校验参数
        # 判断参数是否齐全
        if not all([address_id, pay_method]):
            return http.HttpResponseForbidden('缺少必传参数')

        # 判断address_id是否合法
        try:
            address = Address.objects.get(id=address_id)
        except Exception:
            return http.HttpResponseForbidden('参数address_id错误')

        # 判断pay_method是否合法
        if pay_method not in [OrderInfo.PAY_METHODS_ENUM['CASH'], OrderInfo.PAY_METHODS_ENUM['ALIPAY']]:
            return http.HttpResponseForbidden('参数pay_method错误')

        # 获取登录用户
        user = request.user
        # 生成订单编号：年月日时分秒+用户编号 '20190526165742000000001'
        # order_id = '%s%09d' % (timezone.localtime().strftime('%Y%m%d%H%M%S'), user.id)
        order_id = timezone.localtime().strftime('%Y%m%d%H%M%S') + ('%09d' % user.id)
        # 1. 显示地开启一次事务
        with transaction.atomic():
            # 在数据库操作之前需要指定保存点(保存数据库最初的状态)
            save_id = transaction.savepoint()
            try:
                # 保存订单基本信息 OrderInfo（一）
                order = OrderInfo.objects.create(
                    order_id=order_id,
                    user=user,
                    address=address,
                    total_count=0,
                    total_amount=Decimal(0.00),
                    freight=Decimal(10.00),
                    pay_method=pay_method,
                    # 如果支付方式为"支付宝"，则状态为"待支付"，否则为"待发货"
                    status=OrderInfo.ORDER_STATUS_ENUM['UNPAID']
                    if pay_method == OrderInfo.PAY_METHODS_ENUM['ALIPAY']
                    else OrderInfo.ORDER_STATUS_ENUM['UNSEND']
                )

                # 保存订单商品信息 OrderGoods（多）

                redis_conn = get_redis_connection('carts')

                # 所有的购物车数据，包含了勾选和未勾选：{b'1': b'1', b'2': b'2'}
                redis_cart = redis_conn.hgetall('carts_%s'%user.id)
                redis_selected = redis_conn.smembers('selected_%s'%user.id)

                # 构造购物车中勾选的商品数据{b'1': b'1'}
                new_cart_dict = {}
                for sku_id in redis_selected:
                    new_cart_dict[int(sku_id)] = int(redis_cart[sku_id])
                # 获取被勾选的商品的sku_id
                sku_ids = new_cart_dict.keys()
                # 遍历购物车中被勾选的商品信息
                for sku_id in sku_ids:
                    # 查询SKU信息
                    while True:
                        # 获取要提交的订单的商品的数量
                        sku = SKU.objects.get(id=sku_id)  # 查询商品和库存信息时，不能出现缓存，所以没用filter(id__in=sku_ids)
                        # 1. 读取原始的库存和销量
                        origin_stock = sku.stock
                        origin_sales = sku.sales



                        # 获取要提交的订单的商品的数量
                        sku_count = new_cart_dict[sku.id]
                        # 判断商品数量是否大于库存，如果大于，响应"库存不足"
                        if sku_count > sku.stock:
                            return http.JsonResponse({'code': RETCODE.STOCKERR, 'errmsg': '库存不足'})
                        # # SKU 减少库存，增加销量
                        # sku.stock -= sku_count
                        # sku.sales += sku_count
                        # sku.save()

                        new_stock = origin_stock - sku_count
                        new_sales = origin_sales + sku_count
                        # 如果在更新数据时，原始数据变化了，返回0: 表示有资源抢夺
                        result = SKU.objects.filter(id=sku_id, stock=origin_stock).update(stock=new_stock, sales=new_sales)
                        # 如果下单失败，但是库存足够时，继续下单，直到下单成功或者库存不足为止
                        if result == 0:
                            continue
                        # SPU 加销量
                        sku.spu.sales += sku_count
                        sku.spu.save()

                        # 保存订单商品信息 OrderGoods（多）
                        OrderGoods.objects.create(
                            order=order,
                            sku=sku,
                            count=sku_count,
                            price=sku.price,
                        )
                        # 累加订单商品的数量和总价到订单基本信息表
                        order.total_count += sku_count
                        order.total_amount += (sku_count * sku.price)
                        # 4. 下单成功, 退出循环
                        break

                # 再加最后的运费，保存订单信息
                order.total_amount += order.freight
                order.save()
            except Exception as e:
                transaction.savepoint_rollback(save_id)
                return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '下单失败'})
            # 2. 数据库操作成功，显示地提交一次事务
            transaction.savepoint_commit(save_id)

        selected = tuple(redis_selected)
        # 清除购物车中已结算的商品，此小节未讲到
        p1 = redis_conn.pipeline()
        p1.hdel('carts_%s'%user.id, *selected)
        p1.srem('selected_%s' % user.id, *selected)
        p1.execute()
        # 响应提交订单结果
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '下单成功', 'order_id': order.order_id})


class OrderSettlementView(LoginRequiredMixin, View):
    """结算订单"""

    def get(self, request):
        """查询并展示要结算的订单"""
        # 获取登录用户
        user = request.user

        # 1.查询用户收货地址: 查询登录用户的未被删除的收货地址
        try:
            addresses = Address.objects.filter(user=user, is_deleted=False)
        except Address.DoesNotExist:
            addresses = None
        # 2.查询Redis购物车中被勾选的商品
        redis_conn = get_redis_connection('carts')
        # 所有的购物车数据, 包含了勾选和未勾选 {b'1': b'2', b'2': b'2'}
        redis_cart = redis_conn.hgetall('carts_%s'%user.id)
        # 被勾选的商品的sku_id: [b'1']
        cart_selected = redis_conn.smembers('selected_%s'%user.id)
        # 构造购物车中被勾选的商品的数据 {b'1': b'2'}
        cart = {}

        for sku_id in cart_selected:
            cart[int(sku_id)] = int(redis_cart[sku_id])
        # 3.获取商品详细信息 + 商品数量 + 商品总金额
        # 查询商品信息
        skus = SKU.objects.filter(id__in=cart.keys())

        # 准备初始值
        total_count = 0
        total_amount = Decimal(0.00) # 由于商品价格是Decimal类型的

        # 　遍历 skus,给每个sku补充count(数量)和amount(小计)
        for sku in skus:
            sku.count = cart[sku.id]
            sku.amount = sku.count * sku.price

            # 计算总数量和总金额
            total_count += sku.count
            total_amount += sku.amount
        # 补充运费
        freight = Decimal('10.00')

        context = {
            'addresses': addresses, # 用户地址信息
            'skus': skus, # 用户购物车已勾选的商品数据
            'total_count': total_count, # 商品总数量
            'total_amount': total_amount, # 商品总金额
            'freight': freight, # 运费
            'payment_amount': total_amount + freight, # 实付款 = 商品总金额 + 运费
        }
        return render(request, 'place_order.html', context)